<?php
/**
 * Created by PhpStorm.
 * User: longli
 * VX: isa1589518286
 * Date: 2021/02/23
 * Time: 11:13
 * @link http://www.lmterp.cn
 */

namespace app\common\service\channel;

use app\common\library\Tools;
use app\common\model\ChannelOrders;
use app\common\model\Orders;
use app\common\service\logistics\ChannelService;
use think\facade\Log;

/**
 * 云途
 * Class YunTu
 * @package app\common\service\channel
 */
class YunTu extends BaseChannel
{
    public static $tokenField = [
        'required' => [ // 必填字段
            [
                'type' => 'text',
                'name' => '客户代码',
                'field' => 'customer',
            ],
            [
                'type' => 'text',
                'name' => '密钥',
                'field' => 'code',
            ]
        ],
        'option' => [ // 可选字段
        ],
    ];

    public function getTrackNumber()
    {
        $header = $this->getHeader();
        if(ChannelOrders::hasOrder($this->order, $this->order->track_num))
        {
            $orderSn = $this->order->order_sn;
        }
        else
        {
            // 创建订单
            $requestBody = $this->buildBody($this->getTrackBody());
            $createUrl = "{$this->channel->carrier->api_base_url}/api/WayBill/CreateOrder";
            $response = Tools::curlPost($createUrl, $requestBody, $header);
            if($this->hasError($response, $data, $code) && !in_array($code, ['2005']))
            {
                $this->order->order_status = Orders::ORDER_DECLARE_ERROR;
                $this->order->save();
                return false;
            }
            $orderSn = trim($data[0]['CustomerOrderNumber']);
        }

        // 获取追踪号
        $temp = $attempts = config('api_attempts'); // 尝试次数
        $getUrl = "{$this->channel->carrier->api_base_url}/api/WayBill/GetOrder";
        $responseBody = [];
        do
        {
            $response = Tools::curlGet($getUrl, [
                'OrderNumber' => $orderSn
            ], $header);
            if($attempts < $temp) sleep(1);
        }while($this->hasError($response, $responseBody) && $attempts--);
        // 验证响应信息
        if($this->hasError($response))
        {
            $this->order->order_status = Orders::ORDER_DECLARE_ERROR;
            $this->order->save();
            return false;
        }
        // 更新订单信息
        $track = trim($responseBody['TrackingNumber']);
        $logisticsSn = trim($responseBody['WayBillNumber']);
        if(empty($track)) $track = $logisticsSn;
        $this->order->save([
            'track_num'      => $track,
            'order_status'   => Orders::ORDER_DECLARE_SUCC,
            'track_num_time' => Tools::now(),
            'logistics_name' => $this->channel->carrier->carrier_name,
        ]);
        ChannelService::getInstance()->saveChannelOrders([
            'order_id' => $this->order->order_id,
            'logistics_sn' => $logisticsSn,
            'channel_id' => $this->channel->channel_id,
            'country_code' => $this->order->buyer_country_code,
            'track_num' => $track,
            'weight' => $this->order->weight,
        ]);
        return $track;
    }

    /**
     * 获取追踪号请求体
     * @return array
     * @date 2021/02/23
     * @author longli
     */
    protected function getTrackBody()
    {
        // 支持批量创建，最多10个
        $parcels = [];
        $weight = 0;
        foreach($this->order->detail as $detail)
        {
            $weight += ($detail->declare_weight > 0 ? $detail->declare_weight / 1000 : 0.1) * $detail->qty;
            $parcels[] = [
                'EName' => $detail->declare_en, // 包裹申报名称(英文)必填
                'CName' => $detail->declare_ch, // 包裹申报名称(中文)
                //'HSCode' => '', // 海关编码
                'Quantity' => $detail->qty, // 申报数量,必填
                'UnitPrice' => $detail->declare_price, // 申报价格(单价),单位 USD,必填
                'UnitWeight' => $detail->declare_weight > 0 ? $detail->declare_weight / 1000 : 0.1, // 申报重量(单重)，单位 kg
                //'Remark'    => '', // 订单备注，用于打印配货单
                //'SKU'   => '', // 用于填写商品 SKU，FBA 订单必填
                //'InvoiceRemark' => '', // 配货信息
                //'CurrencyCode' => '', // 申报币种，默认：USD
            ];
        }
        $iossCode = ($account = $this->getAccountById($this->order->account_id)) && isset($account->token->ioss_code)
                ? $account->token->ioss_code
                : "";
        return [[
            'CustomerOrderNumber' => $this->order->order_sn, // 客户订单号
            'ShippingMethodCode' => $this->channel->channel_code, // 运输方式代码
            'PackageCount' => 1, // 运单包裹的件数
            'Weight' => $weight, // 预估包裹总重量，单位 kg
            'IossCode' => $iossCode, // 云途备案识别码或IOSS号
            //'ReturnOption' => 0, // 是否退回, 1-退 回，0-不退回
            //'InsuranceOption' => 0, // 包裹投保类型，0-不参保，1-按件，2-按比
            //'Coverage' => '', // 保险的最高额度，单位 RMB
            'Receiver' => [ // 收件人信息
                'CountryCode' => $this->order->buyer_country_code, // 收件人所在国家，填写国际通用标准 2 位简码
                'FirstName' => Orders::getBuyerName($this->order), // 收件人姓
                //'LastName' => '', // 收件人名字
                //'Company' => '', // 收件人公司名称
                'Street' => $this->order->buyer_address_1, // 收件人详细地址
                'StreetAddress1' => $this->order->buyer_address_2, // 收件人详细地址 1
                'StreetAddress2' => $this->order->buyer_address_3, // 收件人详细地址 2
                'City' => $this->order->buyer_city, // 收件人所在城市
                'State' => $this->order->buyer_province, // 收件人省/州
                'Zip' => $this->order->buyer_post_code, // 收件人邮编
                'Phone' => Orders::getBuyerPhone($this->order), // 收件人电话
                //'HouseNumber' => '', // 收件人街道地址门牌号
                //'Email' => '', // 收件人电子邮箱
            ],
            'Sender' => [ // 发件人信息
                'CountryCode' => $this->sender->country_code, // 发件人所在国家
                'FirstName' => $this->sender->name, // 发件人姓
                //'LastName' => '', // 发件人名
                //'Company' => '', // 发件人公司名称
                'Street' => $this->sender->address, // 发件人详细地址 FBA 必填
                'City' => $this->sender->city, // 发件人所在城市
                'State' => $this->sender->province, // 发件人省/州
                'Zip' => $this->sender->post_code, // 发件人邮编
                'Phone' => $this->sender->phone, // 发件人电话
            ],
            'Parcels' => $parcels,
            /*'ChildOrders' => [[ // 箱子明细信息，FBA 订单必填
                'BoxNumber' => '', // 箱子编号，FBA 订单必填
                'Length' => '', // 预估包裹单边长，单位 cm
                'Width' => '', // 预估包裹单边宽，单位 cm
                'Height' => '', // 预估包裹单边高，单位 cm
                'BoxWeight' => '', // 预估包裹总重量，单位 kg
                'ChildDetails' => [ // 单箱 SKU 信息，FBA 订单必填
                    'SKU' => '', // 用于填写商品 SKU，FBA 订单必填
                    'Quantity' => '', // 申报数量，FBA 订单必填
                ]]
            ]*/
        ]];
    }

    public function cancelOrder()
    {
        $requestBody = $this->buildBody([
            'OrderType' => 3,
            'OrderNumber' => $this->order->track_num,
        ]);
        $url = "{$this->channel->carrier->api_base_url}/api/WayBill/Delete";
        $response = Tools::curlPost($url, $requestBody, $this->getHeader());
        if($this->hasError($response, $responseBody) || trim($responseBody['Status']) != '5012') return false;
        ChannelService::getInstance()->saveChannelOrders([
            'order_id' => $this->order->order_id,
            'channel_id' =>$this->channel->channel_id,
            'track_num' => $this->order->track_num,
            'is_cancel' => ChannelOrders::IS_YES,
        ]);
        return true;
    }

    public function interceptOrder($remark = '')
    {
        $remark = $remark ? : $this->order->remark;
        $url = "{$this->channel->carrier->api_base_url}/api/WayBill/Intercept";
        $requestBody = $this->buildBody([
            'OrderType' => 3,
            'OrderNumber' => $this->order->track_num,
            'Remark' => trim($remark),
        ]);
        $response = Tools::curlPost($url, $requestBody, $this->getHeader());
        if($this->hasError($response, $responseBody) || trim($responseBody['Result']) != '5011') return false;
        $this->order->order_status = Orders::ORDER_EXCEPTION;
        $this->order->save();
        return true;
    }

    public function getLabel()
    {
        $url = "{$this->channel->carrier->api_base_url}/api/Label/Print";
        $requestBody = $this->buildBody([$this->order->track_num]);
        $response = Tools::curlPost($url, $requestBody, $this->getHeader());
        if($this->hasError($response, $responseBody) || !($byte = file_get_contents($responseBody[0]['Url'])))
        {
            $this->order->order_status = Orders::ORDER_LABEL_ERROR;
            $this->order->save();
            return false;
        }
        $this->order->label_url = self::saveLabel($byte);
        $this->order->save();
        ChannelService::getInstance()->saveChannelOrders([
            'order_id' => $this->order->order_id,
            'channel_id' =>$this->channel->channel_id,
            'track_num' => $this->order->track_num,
            'label_url' => $this->order->label_url,
        ]);
        return $this->order->label_url;
    }

    public function getPrice()
    {
        $chOrder = ChannelOrders::get([
            'order_id' => $this->order->order_id,
            'track_num' => $this->order->track_num,
        ]);
        if(empty($chOrder)) return 0;
        $url = "{$this->channel->carrier->api_base_url}/api/Freight/GetShippingFeeDetail";
        $response = Tools::curlGet($url, [
            'WayBillNumber' => $chOrder->logistics_sn
        ], $this->getHeader());
        if($this->hasError($response, $responseBody)) return 0;
        return floatval($responseBody['TotalFee']);
    }

    /**
     * 获取运输方式
     * @param string $countryCode 国家代码，为空则获取所有
     * @return array
     * @date 2021/02/23
     * @author longli
     */
    public function getShippingMethod($countryCode = '')
    {
        $url = "{$this->channel->carrier->api_base_url}/api/Common/GetShippingMethods";
        $param = [];
        if(!empty($countryCode)) $param['CountryCode'] = $countryCode;
        $response = Tools::curlGet($url, $param, $this->getHeader());
        if($this->hasError($response, $responseBody)) return [];
        return $responseBody;
    }

    /**
     * 是否包含错误信息
     * @param array $responseData 响应信息
     * @param array $body 解析后的参数
     * @param string $code API 状态码
     * @return bool
     * @date 2021/02/23
     * @author longli
     */
    protected function hasError($responseData = [], &$body = null, &$code = null)
    {
        if(isset($responseData['status']) && isset($responseData['data']) && is_string($responseData['data']))
        {
            if(!$responseData['status'])
            {
                Log::info(sprintf("渠道【%s】服务器无法请求，错误信息【%s】，订单号【%s】", $this->channel->channel_name, json_encode($responseData['info']), $this->order->order_sn));
                return true;
            }
            $responseData = json_decode($responseData['data'], true);
        }
        if(trim($responseData['Code']) != '0000')
        {
            Log::info(sprintf("渠道【%s】响应错误，错误信息【%s】，状态码【%s】，订单号【%s】",
                $this->channel->channel_name, $responseData['Message'],
                $responseData['Code'],
                $this->order->order_sn));
            return true;
        }
        $body = isset($responseData['Items']) ? $responseData['Items'] : $responseData['Item'];
        $code = $responseData['Code'];
        return false;
    }

    /**
     * 构建请求体
     * @param array $postData
     * @date 2021/02/23
     * @author longli
     */
    protected function buildBody($postData = [])
    {
        return json_encode($postData);
    }

    /**
     * 生成请求头信息
     * @return string[]
     * @date 2021/02/23
     * @author longli
     */
    protected function getHeader()
    {
        $token = base64_encode("{$this->channel->carrier->token->customer}&{$this->channel->carrier->token->code}");
        return [
            'Content-Type:application/json',
            "Authorization:Basic $token",
            'charset:UTF-8'
        ];
    }
}